package com.small.handler;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.log4j.Logger;
import com.small.annotation.Clear;
import com.small.annotation.Controller;
import com.small.annotation.GlobalAspect;
import com.small.annotation.Pointcut;
import com.small.annotation.Transaction;
import com.small.aop.AopCache;
import com.small.aop.AspectProxy;
import com.small.proxy.Proxy;
import com.small.proxy.ProxyFactor;
import com.small.utils.BaseUtil;

/**
 * Aop映射处理类
 * @author 乐乐
 *
 */
public class AopHandler {
  private static final Logger logger = Logger.getLogger(AopHandler.class);
  
  /**
   * 类--存在切面列表方法的映射
   */
  private static Map<Class<?>, List<Method>> classAspectMethodMap = new HashMap<Class<?>, List<Method>>();
  
  /**
   * 方法--方法切面类的映射
   */
  private static Map<Method, List<Class<?>>> methodAspectMap = new HashMap<Method, List<Class<?>>>();
  
  /**
   * Aop初始化
   */
  public static void init() {
    
    Set<Class<?>> classes = ClassHelper.getClassAll();
    
    //获取所有默认拦截器
    List<Class<?>> globalclass = ClassHelper.getGlobalAspectListClass();
    List<Class<?>> globalClass = new ArrayList<Class<?>>();
    //不为空进行全局排序
    if (0 != globalclass.size()) {
      List<Integer> so = new ArrayList<Integer>();
      Map<Integer, Class<?>> map = new HashMap<Integer, Class<?>>();
      for (Class<?> gClass : globalclass) {
        GlobalAspect global = gClass.getAnnotation(GlobalAspect.class);
        int sort = global.sort();
        so.add(sort);
        map.put(sort, gClass);
      }
      Collections.sort(so);
      for (Integer integer : so) {
        Class<?> clazz = map.get(integer);
        globalClass.add(clazz);
      }
    }
    
    //初始化类-(方法-方法切面列表)的映射
    for (Class<?> clazz : classes) {
      
      boolean ifClear = clazz.isAnnotationPresent(Clear.class);
      
      boolean ifPointcut = clazz.isAnnotationPresent(Pointcut.class);
      
      boolean ifTransaction = clazz.isAnnotationPresent(Transaction.class);
      
      Clear cClear = null;
      Class<?>[] cClass = null;
      if (ifClear) {
        cClear = clazz.getAnnotation(Clear.class);
        cClass = cClear.value();   //获取当前类注解的所有需要清除的切面值
      }
      Pointcut cPointcut = null;
      Class<?>[] pClass = null;
      if (ifPointcut) {
        cPointcut = clazz.getAnnotation(Pointcut.class);
        pClass = cPointcut.value();   //获取当前类注解的所有普通切面值
      }
      Transaction cTransaction = null;
      Class<?>[] tClass = null;
      if (ifTransaction) {
        cTransaction = clazz.getAnnotation(Transaction.class);
        tClass = cTransaction.value();   //获取当前类注解的所有事务切面值
      }
      
      //类注解上注解的事务切面类和普通切面类
      List<Class<?>> aspectClass = new ArrayList<Class<?>>();
      
      if (clazz.isAnnotationPresent(Controller.class)) {
        if (0 != globalClass.size())
          aspectClass.addAll(globalClass);
      }
      
      if (null != pClass) 
        aspectClass.addAll(BaseUtil.ArrayChangeList(pClass));
      if (null != tClass)  
        aspectClass.addAll(BaseUtil.ArrayChangeList(tClass));
      
      Method[] methods = clazz.getMethods();    //获取当前类中所有方法
      
      for (Method method : methods) {
        
        //不处理父类object类中的公共方法
        //方法命名规则 如果方法名与下面字符串匹配将不会被代理
        if ("toString" == method.getName()) 
          break;
        if ("hashCode" == method.getName()) 
          break;
        if ("equals" == method.getName()) 
          break;
        if ("getClass" == method.getName()) 
          break;
        if ("notify" == method.getName()) 
          break;
        if ("notifyAll" == method.getName()) 
          break;
        if ("wait" == method.getName()) 
          break;
        if ("finalize" == method.getName()) 
          break;
        
        boolean mifClear = method.isAnnotationPresent(Clear.class);
        
        boolean mifPointcut = method.isAnnotationPresent(Pointcut.class);
        
        boolean mifTransaction = method.isAnnotationPresent(Transaction.class);
        
        if (mifPointcut) {  //判断当前方法是否使用了@Pointcut注解
          
          Pointcut mPointcut = method.getAnnotation(Pointcut.class);
          Class<? extends AspectProxy>[] aspectClasses = mPointcut.value();
          
          //获取当前方法上定义的所有切面列表
          List<Class<?>> aspectClassSet = BaseUtil.ArrayChangeList(aspectClasses);  
          
          //如果当前类使用了注解并且当前类上定义注解的列表不为空就把当前类的切面对象放入当前方法的切面列表中
          aspectClass.addAll(aspectClassSet);
          
          //把当前方法和方法列表加入目标方法--切面列表的映射中
          if (methodAspectMap.containsKey(method)) {  //如果缓存中存在就查出来存进去
            List<Class<?>> mSet = methodAspectMap.get(method);
            mSet.addAll(BaseUtil.ArrayChangeList(pClass));
          }else {                                     //如果没有直接put进去
            methodAspectMap.put(method, aspectClass);
          }
          
        }else {   //如果当前方法没有使用注解  当前类有切面注解   那么当前方法会自动继承当前类的切面注解
          
          //如果当前类使用了注解并且当前类上定义注解的列表不为空就把当前类的切面对象放入当前方法的切面列表中
          if (0 != aspectClass.size()) {  
            //把当前方法和方法列表加入目标方法--切面列表的映射中
            methodAspectMap.put(method, aspectClass);
          }
        }
        
        if (mifTransaction) {    //判断当前方法是否使用了@Transaction注解
          
          Transaction mTransaction = method.getAnnotation(Transaction.class);
          Class<?>[] aspectClasses = mTransaction.value();
          
          List<Class<?>> aspectClassSet = BaseUtil.ArrayChangeList(aspectClasses);  //获取当前方法上定义的所有切面列表
          
          //如果当前类使用了注解并且当前类上定义注解的列表不为空就把当前类的切面对象放入当前方法的切面列表中
          if (0 != aspectClass.size()) {  
            aspectClass.addAll(aspectClassSet);
          }
          //把当前方法和方法列表加入目标方法--切面列表的映射中
          if (methodAspectMap.containsKey(method)) {  //如果缓存中存在就查出来存进去
            List<Class<?>> mSet = methodAspectMap.get(method);
            mSet.addAll(BaseUtil.ArrayChangeList(pClass));
          }else {                                     //如果没有直接put进去
            methodAspectMap.put(method, aspectClass);
          }
          
        }else {   //如果当前方法没有使用注解  当前类有切面注解   那么当前方法会自动继承当前类的切面注解
          
          //如果当前类使用了注解并且当前类上定义注解的列表不为空就把当前类的切面对象放入当前方法的切面列表中
          if (0 != aspectClass.size()) {  
            if (methodAspectMap.containsKey(method)) {  //如果缓存中存在就查出来存进去
              List<Class<?>> mSet = methodAspectMap.get(method);
              for (Class<?> cla : mSet) {
                if (!aspectClass.contains(cla)) {
                  aspectClass.add(cla);
                }
              }
            }else {                                     //如果没有直接put进去
              methodAspectMap.put(method, aspectClass);
            }
          }
        }
        
        if (mifClear) {    //判断当前方法是否使用了@Chear注解
          
          Clear mClear = method.getAnnotation(Clear.class);
          Class<? extends Proxy>[] aspectClasses = mClear.value();
          
          //获取当前方法上定义的所有清除切面列表
          List<Class<?>> aspectClassSet = BaseUtil.ArrayChangeList(aspectClasses);  
          
          //如果当前类使用了Clear注解并且当前类上定义注解的列表不为空就把当前类的清除切面列表放入当前方法的切面列表中
          if (null != cClass) {  
            aspectClassSet.addAll(BaseUtil.ArrayChangeList(cClass));
          }
          
          //把当前方法和方法列表加入目标方法--切面列表的映射中
          if (methodAspectMap.containsKey(method)) {  //如果缓存中存在就查出来然后删除
            List<Class<?>> mSet = methodAspectMap.get(method);
            mSet.removeAll(aspectClassSet);
          }
          
        }
        
        if (null != cClass) {  
          if (methodAspectMap.containsKey(method)) {  //如果缓存中存在就查出来然后删除
            List<Class<?>> mSet = methodAspectMap.get(method);
            mSet.removeAll(BaseUtil.ArrayChangeList(cClass));
          }
        }
        
        
        //判断当前方法是否存在切面列表   如果存在放入类--方法的映射中  如果不存在就无需理会
        if (null != methodAspectMap.get(method)) {
          if (0 != methodAspectMap.get(method).size()) {
            
            //判断缓存中当前类是否有切面列表方法 有就获取value，往里面加   没有就put一个集合
            if (null != classAspectMethodMap.get(clazz)) {   
              classAspectMethodMap.get(clazz).add(method);
              
            }else {
              List<Method> mSet = new ArrayList<Method>();
              mSet.add(method);
              classAspectMethodMap.put(clazz, mSet);
            }
          }
        }
        
      }
      
    }  
    
    //切面类转切面代理  数据放入缓存
    for (Entry<Class<?>, List<Method>> map : classAspectMethodMap.entrySet()) {
      Class<?> clazz = map.getKey();
      List<Method> methods = map.getValue();
      Map<Method, List<Proxy>> mpm = new HashMap<Method, List<Proxy>>();
      for (Method method : methods) {
        List<Class<?>> methodClasses = methodAspectMap.get(method);
        List<Proxy> proxies = new ArrayList<Proxy>();
        for (Class<?> mclass : methodClasses) {
          proxies.add((Proxy)BeanHelper.getBean(mclass));
        }
        mpm.put(method, proxies);
      }
      AopCache.setCache(clazz, mpm);
      Object proxy = ProxyFactor.createProxy(clazz);
      AopCache.setClassProxyCache(clazz, proxy);
      BeanHelper.setBean(clazz, proxy);
      
    }
    logger.debug("AopHandler加载完成....");
  }
  
  
}
